import threading

from pymodbus import Framer
from pymodbus.client import ModbusSerialClient


class ModbusConnector:
    """
    modbus connector
    singleton and thread safety
    """

    _instance = None
    _instance_lock = threading.Lock()

    def __init__(self):
        self.rtu_client: ModbusSerialClient = None

    @classmethod
    def get_instance(cls):
        if not cls._instance:
            # get thread lock
            with cls._instance_lock:
                if not cls._instance:
                    cls._instance = ModbusConnector()
        return cls._instance

    def modbus_rtu_client(self, port: str = "COM3", baud_rate: int = 19200, byte_size: int = 8, parity: str = "N",
                          stop_bits: int = 1, timeout=3):
        """
        modbus rtu client
        :param port:
        :param baud_rate:
        :param byte_size:
        :param parity:
        :param stop_bits:
        :param timeout:
        :return:
        """
        if self.rtu_client is None:
            try:
                self.rtu_client = ModbusSerialClient(port=port, framer=Framer.RTU, baudrate=baud_rate,
                                                     bytesize=byte_size, stopbits=stop_bits, parity=parity,
                                                     timeout=timeout)
                print(f"modbus rtu client initialized: {self.rtu_client}")
            except Exception as e:
                print(f"modbus rtu client initialize failed, exception is: {e}")
        else:
            print(f"modbus rtu client is not None")

    def rtu_read_holding_register(self, start_read_register_address: int = 0, register_count: int = 1,
                                  slave_address: int = 1):
        res = self.rtu_client.read_holding_registers(start_read_register_address, register_count, slave_address)
        return res.registers

    def rtu_write_single_register(self, value: int, start_write_register_address: int = 0, slave_address: int = 1):
        res = self.rtu_client.write_register(address=start_write_register_address, value=value, slave=slave_address)
        return res

    def open_client_device(self):
        self.rtu_client.connect()

    def close_client(self):
        self.rtu_client.close()
